/**
 * @file lj_ringbuffer.c
 * @author Liu Bingyi (cecillearfa@163.com)
 * @brief
 * @version 0.2
 * @date 2023-08-21
 *
 * @copyright Copyright (c) 2023 Liu Bingyi
 *  lj-ringbuffer is licensed under Mulan PubL v2.
 *  You can use this software according to the terms and conditions of the Mulan PubL v2.
 *  You may obtain a copy of Mulan PubL v2 at:
 *           http://license.coscl.org.cn/MulanPubL-2.0
 *  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 *  EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 *  MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *  See the Mulan PubL v2 for more details.
 *
 *
 *                      Mulan Public License，Version 2
 *
 *  Mulan Public License，Version 2 (Mulan PubL v2)
 *
 *  May 2021 http://license.coscl.org.cn/MulanPubL-2.0
 *
 */

#include "lj_ringbuffer.h"
#include <assert.h>
#include <string.h>

int lj_ring_buffer_init(lj_ringbuffer_t* rb, BUFFER_TYPE* pool, size_t pool_size)
{
    rb->buffer = pool;
    rb->buffer_size = pool_size;

    rb->rb_r_index = 0;
    rb->rb_w_index = 0;
    rb->rb_r_mirror = 0;
    rb->rb_w_mirror = 0;

    return 0;
}

int lj_ring_buffer_destroy(lj_ringbuffer_t* rb)
{
    rb->buffer = NULL;
    rb->buffer_size = 0;

    rb->rb_r_index = 0;
    rb->rb_w_index = 0;
    rb->rb_r_mirror = 0;
    rb->rb_w_mirror = 0;
    return 0;
}

size_t lj_ring_buffer_push(lj_ringbuffer_t* rb, const BUFFER_TYPE* src, size_t length)
{
    size_t reserve = 0;
    if (rb->rb_r_mirror == rb->rb_w_mirror) {
        assert(rb->rb_r_index <= rb->rb_w_index);
        reserve = rb->buffer_size - rb->rb_w_index;
        if (reserve >= length) {
            memcpy(&rb->buffer[rb->rb_w_index], src, BUFFER_SIZEOF(length));
            rb->rb_w_index += length;
        } else if (reserve + rb->rb_r_index >= length) {
            memcpy(&rb->buffer[rb->rb_w_index], src, BUFFER_SIZEOF(reserve));

            memcpy(rb->buffer, &src[reserve], BUFFER_SIZEOF(length - reserve));

            rb->rb_w_index = (length - reserve);
            rb->rb_w_mirror = ~rb->rb_w_mirror;
        } else
            length = 0;

    } else {
        assert(rb->rb_r_index >= rb->rb_w_index);
        reserve = rb->rb_r_index - rb->rb_w_index;
        assert(0);
        if (reserve >= length) {
            memcpy(rb->buffer, src, BUFFER_SIZEOF(length));
            rb->rb_w_index += length;
        } else
            length = 0;
    };
    assert(rb->rb_r_index < rb->buffer_size);
    assert(rb->rb_w_index < rb->buffer_size);

    return length;
}

size_t lj_ring_buffer_pull(lj_ringbuffer_t* rb, BUFFER_TYPE* dest, size_t length)
{
    size_t stock = 0;

    if (rb->rb_r_mirror == rb->rb_w_mirror) {
        assert(rb->rb_r_index <= rb->rb_w_index);
        stock = rb->rb_w_index - rb->rb_r_index;
        if (stock >= length) {
            memcpy(dest, &rb->buffer[rb->rb_r_index], BUFFER_SIZEOF(length));
            memset(&rb->buffer[rb->rb_r_index], 0, BUFFER_SIZEOF(length));
            rb->rb_r_index += length;
        } else
            length = 0;
    } else {
        assert(rb->rb_r_index >= rb->rb_w_index);
        stock = rb->buffer_size - rb->rb_r_index;
        if (stock >= length) {
            memcpy(dest, &rb->buffer[rb->rb_r_index], BUFFER_SIZEOF(length));
            memset(&rb->buffer[rb->rb_r_index], 0, BUFFER_SIZEOF(length));
            rb->rb_r_index += length;
        } else if (stock + rb->rb_w_index > length) {
            memcpy(dest, &rb->buffer[rb->rb_r_index], stock);
            memset(&rb->buffer[rb->rb_r_index], 0, BUFFER_SIZEOF(stock));

            memcpy(&dest[stock], rb->buffer, length - stock);
            memset(&rb->buffer[rb->rb_r_index], 0, BUFFER_SIZEOF(length - stock));

            rb->rb_r_index = length - stock;
            rb->rb_r_mirror = ~rb->rb_r_mirror;
        } else
            length = 0;
    }

    assert(rb->rb_r_index < rb->buffer_size);
    assert(rb->rb_w_index < rb->buffer_size);

    return length;
}
